package com.huajin.codetest.lock;

/**
 * https://cloud.tencent.com/developer/article/1794961
 * https://bbs.huaweicloud.com/blogs/405053
 * https://www.xttblog.com/?p=3218
 * 
 * synchronized在jdk1.6之前是重量级锁，借助操作系统的Mutex Lock（互斥锁）来实现线程同步
 * synchronized在jdk1.6 对锁的实现引入了大量的优化，如偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销。
 * 锁主要存在四种状态，依次是：无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态，他们会随着竞争的激烈而逐渐升级。
 * 注意锁可以升级不可降级，这种策略是为了提高获得锁和释放锁的效率。
 * 
 * JVM基于进入和退出Monitor对象来实现方法同步和代码块同步
 * 
 * 可重入锁
 * 
 * 可重入就是说某个线程已经获得某个锁，可以再次获取锁而不会出现死锁。
 * 
 * 在计算机科学领域中，可重入性（Reentrancy）是指一个子程序（函数、方法）可以被多个并发执行的线程同时调用，而不会出现不正确的结果。
 * 换句话说，可重入性是指一个子程序可以被多次调用，而每次调用都能正常执行，不会受到之前调用的影响。
 * 可重入性是并发编程中的重要概念，因为在多线程环境下，多个线程可能同时访问同一个资源，如果资源不具备可重入性，那么在并发执行的情况下可能会导致数据不一致、死锁等问题。
 * 
 * 在Java语言中，synchronized是一种重要的同步机制，它可以用来保护共享资源的访问，避免多个线程同时修改共享资源导致的数据不一致问题。
 * 同时，synchronized也具备可重入性，即同一个线程可以重复获得已经持有的锁，而不会造成死锁或其他错误。
 * 
 * synchronized是基于原子性的内部锁机制，是可重入的，因此在一个线程调用synchronized方法的同时在其方法体内部调用该对象另一个synchronized方法，
 * 也就是说一个线程得到一个对象锁后再次请求该对象锁，是允许的，这就是synchronized的可重入性
 */
public class SynchronizedTest {
	
	/**
	 * 通过使用monitorenter和monitorexit指令实现的
	 * monitorenter指令是在编译后插入到同步代码块的开始位置,
	 * monitorexit指令是在编译后插入到同步代码块的结束处或异常处
	 */
	public void codeBlock() {
        synchronized (SynchronizedTest.class) {
            System.out.println(Thread.currentThread().getName() + "synchronized code block");
        }
    }

	/**
	 * ACC_SYNCHRONIZED修饰
	 */
    public synchronized void method() {
        System.out.println(Thread.currentThread().getName() + "synchronized method");
    }
    
    /*
     * 外层方法开始执行内层方法执行外层方法执行结束```
     * 从输出结果可以看出，`outer`方法和`inner`方法都成功地执行了，而且是按照预期的顺序执行的。
     * 这是因为在Java中，每个对象都有一个与之关联的监视器（monitor），也可以称之为锁。
     * 当一个线程进入一个被`synchronized`修饰的方法时，它会尝试获取该方法所属对象的锁。
     * 如果该锁没有被其他线程占用，那么当前线程就可以获取到锁，并继续执行方法体中的代码。
     * 在方法执行期间，如果该方法再次调用了其他`synchronized`方法，它可以重复获取同一个锁，而不会被阻塞。
     * 只有当方法执行结束，线程释放锁之后，其他线程才有机会获取到该锁。
     * 
     * 可重入性的实现原理
     * 可重入性的实现原理是通过给每个锁分配一个持有线程和一个计数器来实现的。
     * 当一个线程第一次获取锁时，计数器会被设置为1，表示该线程持有了一次锁。
     * 当同一个线程再次获取同一个锁时，计数器会递增，表示该线程持有了多次锁。
     * 只有当计数器归零时，锁才会被释放。
     * 
     * 在Java中，synchronized关键字就是通过这种方式实现可重入性的。
     * 每个对象都有一个与之关联的监视器（monitor），当一个线程获取到锁时，会将锁的持有线程设置为当前线程，并将计数器设置为1。
     * 当同一个线程再次获取到该锁时，计数器会递增。在方法执行期间，每次递归调用都会增加计数器的值，而在方法执行结束时，计数器会递减。
     * 只有当计数器归零时，锁才会被释放，其他线程才有机会获取到该锁。
     * 
     *  synchronized的可重入性示例
     *  为了更好地理解synchronized的可重入性，我们可以通过一个示例来演示。
     *  
     *  只有一个synchronizedTest对象锁
    */
    public synchronized void outer() {
        System.out.println(Thread.currentThread().getName() + "外层方法开始执行");
        inner();
        System.out.println(Thread.currentThread().getName() + "外层方法执行结束");
    }

    public synchronized void inner() {
        System.out.println(Thread.currentThread().getName() + "内层方法执行");
    }

    /*
     * 外层方法开始执行
     * 内层方法执行
     * 外层方法执行结束```
     * 
     * 从输出结果可以看出，outer方法和inner方法都成功地执行了，而且是按照预期的顺序执行的。
     * 
     * 这是因为当一个线程进入一个被synchronized修饰的方法时，它会尝试获取该方法所属对象的锁。
     * 如果该锁没有被其他线程占用，那么当前线程就可以获取到锁，并继续执行方法体中的代码。
     * 在方法执行期间，如果该方法再次调用了其他synchronized方法，它可以重复获取同一个锁，而不会被阻塞。
     * 只有当方法执行结束，线程释放锁之后，其他线程才有机会获取到该锁。
     * 
     * synchronized关键字在Java中被用于实现线程同步，它的可重入性是指当一个线程获得了一个对象的锁后，再次请求该对象锁时，仍然可以获得该锁。
     * 换句话说，可重入性允许线程在持有锁的情况下再次进入由同一把锁保护的同步代码块。
     * 
     * 可重入性的好处包括：
     * 1. 简化编程：可重入性使得编写递归函数或嵌套调用同步方法更加简单。在递归调用中，线程可以多次获得同一把锁，而不会出现死锁或其他错误。
     * 2. 避免死锁：当一个线程已经持有一个锁时，如果再次请求该锁，由于可重入性，请求会立即成功，不会导致线程阻塞。这样可以避免由于线程间相互等待对方释放锁而导致的死锁。
     *  3. 灵活性：可重入性允许线程在同步代码块内部调用其他需要同步的方法，而不需要重新获取锁。这样可以提高代码的灵活性和可复用性。
     *  
     *  总的来说，synchronized的可重入性使得多线程编程更加简单和灵活，并且可以避免一些潜在的并发问题，提高程序的性能和可靠性。
    */
    public static void main(String[] args) {
    	 new SynchronizedTest().outer();
    }
    
    
    
    //-- 锁重入：所谓锁重入，就是支持正在持有锁的线程支持再次获取锁，不会出现自己锁死自己的问题
    
    //-- 锁消除
    public void businessForLock() {
        // lock对象方法内部创建，线程私有的，根本不会引起竞争
        Object lock = new Object();
        synchronized(lock) {
            // i++;
             //j++;
             // 其它业务操作       
        }    
    }

    public void business() {
    	//i++;
    	//j++;
    	// 其它业务操作
    }
    
    
    
    //-- 锁升级（锁膨胀）：希望能尽量花费最小的代价能达到目的
    //-- 所谓的锁膨胀是指 synchronized 从无锁升级到偏向锁，再到轻量级锁，最后到重量级锁的过程，它叫做锁膨胀也叫做锁升级
    
    
    
    
    
    
    //-- 锁消除、锁粗化、自适应自旋锁
    
    
    
    
    
    
    
	
}
